package bma.common.langutil.ai.callback;

import java.util.LinkedList;
import java.util.Queue;

import bma.common.langutil.core.Preconditions;

/**
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 * @param <RESP_TYPE>
 */
public class AICallbackList<RESP_TYPE> {

	// The runnable,executor pairs to execute.
	private final Queue<AICallback<RESP_TYPE>> callbacks = new LinkedList<AICallback<RESP_TYPE>>();

	private AIResultObject<RESP_TYPE> result;

	/** Creates a new, empty {@link ExecutionList}. */
	public AICallbackList() {
		super();
	}

	public int size() {
		return callbacks.size();
	}

	public boolean isEmpty() {
		return callbacks.isEmpty();
	}

	public AICallback<RESP_TYPE> first() {
		return callbacks.peek();
	}

	public boolean add(AICallback<RESP_TYPE> callback) {
		Preconditions.checkNotNull(callback);

		boolean executeImmediate = false;

		synchronized (callbacks) {
			if (result == null) {
				callbacks.add(callback);
			} else {
				executeImmediate = true;
			}
		}

		if (executeImmediate) {
			if (result.getThrowable() != null) {
				callback.onFailure(result.getThrowable());
			} else {
				callback.onSuccess(result.getResult());
			}
			return true;
		}
		return false;
	}

	public void execute(RESP_TYPE r, Throwable t) {
		// Lock while we update our state so the add method above will finish
		// adding
		// any listeners before we start to run them.
		synchronized (callbacks) {
			if (result != null) {
				return;
			}
			result = new AIResultObject<RESP_TYPE>(r, t);
		}

		while (!callbacks.isEmpty()) {
			AICallback<RESP_TYPE> callback = callbacks.poll();
			try {
				if (result.getThrowable() != null) {
					callback.onFailure(result.getThrowable());
				} else {
					callback.onSuccess(result.getResult());
				}
			} catch (Exception e) {
				callback.onFailure(e);
			}
		}
	}

}
